package com.javacoo.limit.client.internal.redis;

import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.util.StreamUtils;

import com.google.common.collect.ImmutableList;
import com.javacoo.limit.client.api.RateLimiter;
import com.javacoo.limit.client.exception.LimitException;

import lombok.extern.slf4j.Slf4j;

/**
 * 抽象限流接口实现
 * <li></li>
 *
 * @author: duanyong@jccfc.com
 * @since: 2021/5/29 9:35
 */
@Slf4j
public abstract class AbstractRateLimiter implements RateLimiter {
    /**
     * RedisScript
     */
    protected RedisScript<Long> redisLuaScript;
    /**
     * RedisTemplate
     */
    @Autowired
    protected RedisTemplate<String, Serializable> redisTemplate;
    @PostConstruct
    public void initLUA() {
        redisLuaScript = new DefaultRedisScript<>(buildLuaScript(), Long.class);
    }

    /**
     * 尝试获取
     * <p>说明:</p>
     * <li></li>
     *
     * @param keys key
     * @param count 限制数量
     * @return boolean 是否成功
     * @author duanyong
     * @date 2021/5/6 23:09
     */
    @Override
    public boolean tryAcquire(ImmutableList<String> keys , int count) {
        return tryAcquire(keys,count,RateLimiter.DEFAULT_PERIOD);
    }

    /**
     * 尝试获取
     * <p>说明:</p>
     * <li></li>
     *
     * @param keys key
     * @param count  限制数量
     * @param period 时间周期
     * @return boolean 是否成功
     * @author duanyong
     * @date 2021/5/7 23:09
     */
    @Override
    public boolean tryAcquire(ImmutableList<String> keys ,int count, int period) {
        return tryAcquire(keys,count,period,RateLimiter.DEFAULT_TIME_UNIT);
    }

    /**
     * 尝试获取
     * <p>说明:</p>
     * <li></li>
     *
     * @param keys key
     * @param count    限制数量
     * @param period   时间周期
     * @param timeUnit 时间周期单位
     * @return boolean 是否成功
     * @author duanyong
     * @date 2021/5/7 23:09
     */
    @Override
    public boolean tryAcquire(ImmutableList<String> keys ,int count, int period, TimeUnit timeUnit) {
        return acquire(keys,count,period,timeUnit);
    }
    /**
     * 构建lua脚本
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/5/29 9:43
     * @return: java.lang.String
     */
    protected abstract String buildLuaScript();
    /**
     * 尝试获取
     * <p>说明:</p>
     * <li></li>
     * @author duanyong
     * @date 2021/5/7 22:21
     *
     * @param keys key
     * @param limitCount    限制数量
     * @param limitPeriod   时间周期
     * @param timeUnit 时间周期单位
     * @return boolean 是否成功
     */
    protected abstract boolean acquire(ImmutableList<String> keys ,int limitCount, int limitPeriod, TimeUnit timeUnit);
    /**
     * 加载lua脚本
     * <li></li>
     * @author duanyong@jccfc.com
     * @date 2021/5/29 10:47
     * @param path:
     * @return: java.lang.String
     */
    protected String loadLuaScript(String path){
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader());
            Resource[] resource = resolver.getResources(path);
            String luaScriptContent = StreamUtils.copyToString(resource[0].getInputStream(), StandardCharsets.UTF_8);
            log.info("完成加载Lua脚本:{}",luaScriptContent);
            return luaScriptContent;
        } catch (IOException ioException) {
            ioException.printStackTrace();
            throw new LimitException("加载Lua脚本异常",ioException);
        }
    }

}
